package Game;

import java.util.LinkedList;
import java.util.Observable;

import Snake.Barrier;
import Snake.Cell;
import Snake.Power;
import Snake.Snake;

public class Game extends Observable {

	private Snake snake;
	private LinkedList<Snake> snakeArray;
	private LinkedList<Cell> occupiedCells;
	private LinkedList<Cell> allGameCells;
	private LinkedList<Barrier> barriers;
	private LinkedList<Power> powers;
	private LinkedList<Power> foodPowers;
	private LinkedList<Power> jumpPowers;
	private LinkedList<Power> boostPowers;
	private LinkedList<Thread> snakeThreads;

	private int maxNumBarriers;
	private int maxNumItems;
	private int numColumns;
	private int numLines;
	private int minNumBarriers;
	private int minNumItems;
	private int numPlayers;

	private int waitPlayers;
	private int numSnakes;

	private int width;
	private int height;
	private int blockSize;

	public Game() {
		snakeArray = new LinkedList<Snake>();
		occupiedCells = new LinkedList<Cell>();
		allGameCells = new LinkedList<Cell>();
		barriers = new LinkedList<Barrier>();
		powers = new LinkedList<Power>();
		foodPowers = new LinkedList<Power>();
		jumpPowers = new LinkedList<Power>();
		boostPowers = new LinkedList<Power>();
		snakeThreads = new LinkedList<Thread>();

		waitPlayers = 0;
		numSnakes = 0;
	}

	public void informationFromServer(int numPlayers, int maxNumBarriers,
			int maxNumItems, int numColumns, int numLines) {
		this.numPlayers = numPlayers;
		this.maxNumBarriers = maxNumBarriers;
		this.maxNumItems = maxNumItems;
		this.numColumns = numColumns;
		this.numLines = numLines;
		this.minNumBarriers = maxNumBarriers / 2;
		this.minNumItems = maxNumItems / 2;
		this.blockSize = 20;
		this.width = (numColumns * blockSize) + 6;
		this.height = (numColumns * blockSize) + 28;
		setAllStates();
		createGame();
	}

	public void createGame() {
		int random = (int) (Math.random() * maxNumBarriers);
		while (random < minNumBarriers) {
			random = (int) (Math.random() * maxNumBarriers);
		}
		spawnBarriers(random);
		int random2 = (int) (Math.random() * maxNumItems);
		while (random2 < minNumItems) {
			random2 = (int) (Math.random() * maxNumItems);
		}
		spawnPowers(random2);
		separatePowers();
	}

	private void separatePowers() {
		for (int i = 0; i < powers.size(); i++) {
			int random = (int) (Math.random() * 100);
			if (random <= 33) {
				boostPowers.add(powers.get(i));
			} else if (random > 33 && random <= 66) {
				foodPowers.add(powers.get(i));
			} else if (random > 66 && random <= 100) {
				jumpPowers.add(powers.get(i));
			}
		}
	}

	private void spawnPowers(int random) {
		for (int i = 0; i < random; i++) {
			int randomX = (Math.round(((int) (Math.random() * width))
					/ blockSize))
					* blockSize;
			int randomY = (Math.round(((int) (Math.random() * height))
					/ blockSize))
					* blockSize;
			Power newP = new Power(new Cell(randomX, randomY), "");
			if (powers.size() > 0 && barriers.size() > 0) {
				for (int b = 0; b < barriers.size(); b++) {
					for (int a = 0; a < powers.size(); a++) {
						while ((newP.getCell().getX() == powers.get(a)
								.getCell().getX() && newP.getCell().getY() == powers
								.get(a).getCell().getY())
								|| (newP.getCell().getX() == barriers.get(b)
										.getCell().getX() && newP.getCell()
										.getY() == barriers.get(b).getCell()
										.getY())) {
							randomX = (Math
									.round(((int) (Math.random() * width))
											/ blockSize))
									* blockSize;
							randomY = (Math
									.round(((int) (Math.random() * height))
											/ blockSize))
									* blockSize;
						}
						newP = new Power(new Cell(randomX, randomY), "");
					}
				}
			}
			powers.add(newP);
		}

	}

	private void spawnBarriers(int random) {
		for (int i = 1; i < random; i++) {
			int randomX = (Math.round(((int) (Math.random() * width))
					/ blockSize))
					* blockSize;
			int randomY = (Math.round(((int) (Math.random() * height))
					/ blockSize))
					* blockSize;
			Barrier newB = new Barrier(new Cell(randomX, randomY), "");
			if (barriers.size() > 0) {
				for (int a = 0; a < barriers.size(); a++) {
					while (newB.getCell().getX() == barriers.get(a).getCell()
							.getX()
							&& newB.getCell().getY() == barriers.get(a)
									.getCell().getY()) {
						randomX = (Math.round(((int) (Math.random() * width))
								/ blockSize))
								* blockSize;
						randomY = (Math.round(((int) (Math.random() * height))
								/ blockSize))
								* blockSize;
					}
					newB = new Barrier(new Cell(randomX, randomY), "");
				}
			}
			barriers.add(newB);
		}
	}

	public void setStates() {
		synchronized (this) {
			fillAllTheCells();
			setChanged();
			notifyObservers();
		}
	}

	public void tellSnakeToClose(int id) {
		for (Snake s : snakeArray) {
			if (s.getSnakeId() == id) {
				s.setClosedByGUI(true);
				s.setRunning(false);
			}
		}
	}

	public int waitForAllSelectedPlayers() {
		if (waitPlayers >= numPlayers) {
			return waitPlayers;
		} else {
			waitPlayers++;
		}
		return waitPlayers;
	}

	public int numSnakesClosed(boolean toRemove) {
		if (toRemove) {
			numSnakes++;
		}
		return numSnakes;
	}

	public/* synchronized */void addNewSnakeToGame(int id) {
		switch (id) {
		case 1:
			for (Cell c : allGameCells) {
				if (c.getX() == (width - 6) / 2 && c.getY() == 0) {
					c.setId(id);
					snake = new Snake(id, c, this);
					snakeArray.add(snake);
					Thread t = new Thread(snake);
					snakeThreads.add(t);
					t.start();
				}
			}
			break;
		case 2:
			for (Cell c : allGameCells) {
				if (c.getX() == 0 && c.getY() == (height - 28) / 2) {
					c.setId(id);
					snake = new Snake(id, c, this);
					snakeArray.add(snake);
					Thread t = new Thread(snake);
					snakeThreads.add(t);
					t.start();
				}
			}
			break;
		case 3:
			for (Cell c : allGameCells) {
				if (c.getX() == (width - 6) / 2
						&& c.getY() == (height - 28) - blockSize) {
					c.setId(id);
					snake = new Snake(id, c, this);
					snakeArray.add(snake);
					Thread t = new Thread(snake);
					snakeThreads.add(t);
					t.start();
				}
			}
			break;
		case 4:
			for (Cell c : allGameCells) {
				if (c.getX() == (width - 6) - blockSize
						&& c.getY() == (height - 28) / 2) {
					c.setId(id);
					snake = new Snake(id, c, this);
					snakeArray.add(snake);
					Thread t = new Thread(snake);
					snakeThreads.add(t);
					t.start();
				}
			}
			break;
		}
	}

	public /*synchronized*/ void fillAllTheCells() {
		occupiedCells.clear();
		for (Snake s : snakeArray) {
			checkJumpBoostPower(s);
			for (int i = 0; i < s.getOccupiedCells().size(); i++) {
				occupiedCells.add(s.getOccupiedCells().get(i));
			}
		}
	}

	public void checkJumpBoostPower(Snake s) {
		if (s.isJumpBoost()) {
			for (int i = 0; i < s.getOccupiedCells().size(); i++) {
				s.getOccupiedCells().get(i).setJumpBoost(true);
				occupiedCells.add(s.getOccupiedCells().get(i));
			}
		}
	}

	public boolean CLickSnake(Cell cell) {
		for (Snake s : snakeArray) {
			for (int i = 0; i < s.getOccupiedCells().size(); i++) {
				if (s.getOccupiedCells().get(i).getX() / blockSize == cell
						.getX()
						&& s.getOccupiedCells().get(i).getY() / blockSize == cell
								.getY()) {
					cell.setId(s.getOccupiedCells().get(i).getId());
					if (!s.isAllowSnakeToMove()) {
						s.getThreadGroup().interrupt();
					}
					return true;
				}
			}
		}
		return false;
	}

	public void setSnakeDirections(Cell cell) {
		for (Snake s : snakeArray) {
			if (s.getSnakeId() == cell.getId()) {
				s.setDirections(cell);
			}
		}
	}

	public void setAllStates() {
		allGameCells.clear();
		for (int i = 0; i < width - blockSize; i += blockSize) {
			for (int j = 0; j < height - 2 * blockSize; j += blockSize) {
				allGameCells.add(new Cell(j, i));
			}
		}
	}

	public int setWinningMessage(int snakeId) {
		return snakeId;
	}

	public LinkedList<Cell> getOccupiedCells() {
		return occupiedCells;
	}

	public LinkedList<Cell> getAllGameCells() {
		return allGameCells;
	}

	public LinkedList<Barrier> getBarriers() {
		return barriers;
	}

	public LinkedList<Power> getPowers() {
		return powers;
	}

	public LinkedList<Power> getFoodPowers() {
		return foodPowers;
	}

	public LinkedList<Power> getJumpPowers() {
		return jumpPowers;
	}

	public LinkedList<Power> getBoostPowers() {
		return boostPowers;
	}

	public int getWidth() {
		return width;
	}

	public int getHeight() {
		return height;
	}

	public int getBlockSize() {
		return blockSize;
	}

	public int getNumLines() {
		return numLines;
	}

	public int getNumColumns() {
		return numColumns;
	}

	public int getMaxNumBarriers() {
		return maxNumBarriers;
	}

	public int getMaxNumItems() {
		return maxNumItems;
	}

	public int getNumPlayers() {
		return numPlayers;
	}
}
